/*
 * Copyright (c) 2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "Debug.h"
#include <stdio.h>

using namespace alkes;

int local_debug_print(const char *format, va_list arg);
static alkes_error_callback s_old_callback = 0;
static Deque* s_log_text = 0;
static bool s_dirty = true;

Debug::Debug()
: glyph_provider_(0)
, text_(0)
{
}

Debug::Debug(IGraphics* graphics, uint16_t height)
: glyph_provider_(0)
, text_(0)
{
    construct(graphics, height);
}

//#define USE_PLACEMENT_NEW

Debug::~Debug()
{
    alkes_set_error_callback(s_old_callback);
    s_old_callback = 0;

    std::string* p;
    while (0 != (p = reinterpret_cast<std::string*>(logs_.shift())))
    {
        delete p;
    }

    s_log_text = 0;

    // free memory
#if defined(USE_PLACEMENT_NEW)
    uint16_t i;
    void* p = text_;

    for (i = 0; i < max_count_; ++i)
    {
        text_[i].~TextNode();
    }
    alkes_free(p);

#else
    if (text_)
        delete [] text_;
#endif
}

void Debug::construct(IGraphics* graphics, uint16_t height)
{
    if (0 == glyph_provider_)
    {
        glyph_provider_ = EmbedGlyphProvider::create();
    }

    if (0 != glyph_provider_)
    {
        //alkes_set_error_level(AL_ERRLV_ALL);
        s_old_callback = alkes_set_error_callback(local_debug_print);
    }
    else
        return ;

    IGlyphProvider::TextMetrics metrics = glyph_provider_->getTextMetrics();

    height_ = height;
    metrics_height_ = metrics.height;
    max_count_ = height / metrics_height_ - 2;
    uint16_t i;

#if defined(USE_PLACEMENT_NEW)
    uint8_t* p = (uint8_t*)alkes_malloc(sizeof(TextNode) * max_count_);
    for (i = 0; i < max_count_; ++i)
    {
        new(p + sizeof(TextNode) * i) TextNode();
    }
    text_ = (TextNode*)p;
#else
    text_ = new TextNode[max_count_];
#endif

    for (i = 0; i < max_count_; ++i)
    {
        addChild(text_ + i);
    }

    graphics_ = restrict_cast<IGraphics2D*>(graphics);

    s_log_text = &logs_;
    index_ = 0;
    count_ = 0;
}

bool Debug::onUpdate()
{
    uint16_t i = index_;
    uint16_t n;
    uint16_t count = 0;
    std::string* p;

    while (0 != (p = reinterpret_cast<std::string*>(logs_.shift())))
    {
        TextNode::construct(
            text_[i % max_count_], graphics_, glyph_provider_, p->c_str(), Color::SILVER);

        ++i;
        ++count;
        delete p;
    }

    //return true;

    //AL_INFO("count=%d", count);

    count_ = max_count_ < (count_ + count) ? max_count_ : (count_ + count);
    index_ = i % max_count_;

    if (!count_)
        return true;

    uint16_t start = (i + max_count_ - count_) % count_;

    uint16_t top = 0;
    for (n = 0; n < count_; ++n, ++start)
    {
        text_[start % max_count_].setDest(UPoint2D(0, top));
        top += metrics_height_;
    }

    if (s_dirty)
    {
        //AL_TRACE();
        setDirty(true);
        s_dirty = false;
    }

    return true;
}

int local_debug_print(const char *format, va_list arg)
{
    if (!s_log_text)
        return 0;

    if (s_old_callback)
    {
        s_old_callback(format, arg);
    }

    char str[256];
    int len = vsnprintf(str, sizeof(str), format, arg);

    if (len <= 0)
        return 0;

    if (sizeof(str) < len)
        len = sizeof(str);

    std::string* log = new std::string(str);
    s_log_text->push(log);

    s_dirty = true;

    return 0;
}
